package model.decoder;

import java.util.ArrayList;

import model.OnScreen;
import model.camera.CameraManager;
import model.reference.ReferencePoint;

import com.vandorenmes.argusv6_5.R;

import android.hardware.Camera.Size;
import android.os.AsyncTask;

/**
 * Superclass for decoding barcodes
 * <p> Extends AsyncTask, allowing asynchronous decoding.
 *
 * @param <ResultType> The class of the object returned when a code is deciphered. 
 */
public abstract class Decoder<ResultType> extends AsyncTask<byte[], Void, ResultType[]>
{
	private static final String TAG = Decoder.class.getSimpleName();
	
	protected Size _previewSize;
	
	@Override
	protected ResultType[] doInBackground(byte[]... params) 
	{
		Thread.currentThread().setName(String.format("AsyncTask: %s", TAG));
		return null;
	}

	@Override
	protected void onPostExecute(ResultType[] results) 
	{
		ArrayList<ReferencePoint> refPoints = new ArrayList<ReferencePoint>();
		
		if(hasResult(results))
		{
			for (ResultType result : results) 
            {
				if(result == null)
					continue;
				//If decoded value contains coordinates, convert to reference point and add it to the list.
				if(isRightFormat(getDecodedData(result)))
					refPoints.add(createReferencePoint(result));
				else
					OnScreen.getInstance().notifyScreenWithToast(R.string.invalid_data); // "Invalid code found"
            }
		}
		
		OnScreen.getInstance().updateReferencePointList(refPoints);
		
		//Allow detection of new codes
		CameraManager.addCallbackBuffer();
		
		//Send a hint to garbage collector.
		System.gc();
	}
	
	/**
	 * Gets a string representation of the data encoded in the barcode.
	 * @param result The barcode found.
	 * @return The string value of the decoded data.
	 */
	protected abstract String getDecodedData(ResultType result);
	
	/**
	 * Creates a referencepoint out of found barcode.
	 * @param result The barcode found.
	 * @return The referencepoint object created.
	 */
	protected abstract ReferencePoint createReferencePoint(ResultType result);
	
	/**
	 * Checks whether the processed screen contains barcodes.
	 * @param results The array of possible barcodes.
	 * @return True if the processed screen contains 1 or more barcodes.
	 */
	protected boolean hasResult(ResultType[] results)
	{
		if(results == null)
			return false;
		if(results.length == 1)
			OnScreen.getInstance().notifyScreenWithToast(R.string.single_code); // "1 code"
		
		return results.length >= 1;
	}
	
	/**
	 * Checks whether the given string contains a coordinate.
	 * @param result The string to check
	 * @return True if the string contains a coordinate.
	 */
	protected boolean isRightFormat(String result)
	{
		return result.matches("[-]?[0-9]+[_][-]?[0-9]+[_][-]?[0-9]+");
	}
}
